package ngat.oss;

import ngat.message.OSS.*;
import ngat.message.RCS_TCS.*;
import ngat.message.base.*;
import ngat.instrument.*;
import ngat.util.*;
import ngat.util.logging.*;
import ngat.astrometry.*;
import ngat.net.*;
import ngat.phase2.*;

import java.util.*;
import java.io.*;
import java.net.*;
import java.text.*;

/** Simulates a scheduling run by sending the appropriate requests to the OSS.
 * <ul>
 *  <li> TEL_INFO </li>
 *  <li> INST_INFO </li>
 *  <li> SCHEDULE </li>
 *  <li> UPDATE_GROUP </li>
 * </ul>
 * The telescope position is updated each time an observation group is completed
 * and the DB is updated with a randomly selected succ or failure event.
 */
public class ScheduleSim implements Runnable {


    public static final int    DEFAULT_PORT = 7910;
    
    public static final String DEFAULT_HOST = "localhost";
    
    public static final double DEFAULT_LAT  = 28.76;
    
    public static final double DEFAULT_LONG = -17.88;

    
    protected String proxyHost;

    protected int    proxyPort;

    protected double latitude;

    protected double longitude;

    protected double currAz;
    
    protected double currAlt;

    /** Create a ScheduleSim.
     */
    public ScheduleSim() {
	
	
    }
    
    /** Load config properties from a file.*/
    public void configure(File file) 
	throws IOException, IllegalArgumentException {
	ConfigurationProperties config = new ConfigurationProperties();
	config.load(new FileInputStream(file));
	configure(config);
    }
    
    /** Load config properties.*/
    public void configure(ConfigurationProperties config) 
	throws IOException, IllegalArgumentException { 
	latitude = config.getDoubleValue("latitude",  DEFAULT_LAT);
	longitude= config.getDoubleValue("longitude", DEFAULT_LONG);
	Position.setViewpoint(Math.toRadians(latitude), Math.toRadians(longitude));
	
	proxyHost = config.getProperty("proxy.host", DEFAULT_HOST);
	proxyPort = config.getIntValue("proxy.port", DEFAULT_PORT);
	
    }
    
    /** Loads config properties into a new ScheduleSim and starts it off.*/
    public static void main(String args[]) {
	
	ScheduleSim sim = new ScheduleSim();
	
	try {
	    sim.configure(new File(args[0]));
	} catch (Exception ex) {
	    System.err.println("Error starting ScheduleSim: "+ex);
	    return;
	}
	
	(new Thread(sim)).start();

    }
	

	
    /** Starts the simulation run.*/
    public void run() {
	
	// Init. Telescope position.
	currAz  = Math.toRadians(Math.random()*360.0);
	currAlt = Math.toRadians(Math.random()*90.0);

	int count = 0; // count groups executed.

	(new SeeingSimulator()).start();

	// First time in - set false if no group is returned by scheduler..
	boolean valid = true;
	long    pause = 0L;

	while (true) {
	    	    
	    if (valid) 
		pause = 10000L;
	    else 
		pause = 300000L;
		
	    try {Thread.sleep(pause); } catch (InterruptedException ix) {}
	    
	    Position sun     = Astrometry.getSolarPosition();
	    double   sunElev = sun.getAltitude();
	    boolean  daytime = (sunElev > 0.0); 
	    System.err.println("Sun elevation: "+Position.toDegrees(sunElev, 3));
	    
	    if (daytime) 		
		continue;	    
	    
	    count++;
	    
	    System.err.println("***Starting Group #"+count);

	    // TEL_INFO
	    TEL_INFO telInfo = new TEL_INFO("TEL:"+count);
	    
	    // May need to translate deg/rad ?	
	    telInfo.setTelescopeAzimuth(currAz);//rads
	    telInfo.setTelescopeAltitude(currAlt);//rads
	    telInfo.setAutoguiderAvailable(true);
	    telInfo.setAzNegLimit(Math.toRadians(-180.0)); //rads -cfg
	    telInfo.setAzPosLimit(Math.toRadians(360.0)); //rads -cfg
	    telInfo.setAltLowLimit(Math.toRadians(20.0));
	    telInfo.setAltHighLimit(Math.toRadians(85.0));
	    telInfo.setRotNegLimit(Math.toRadians(-270.0)); //rads -cfg
            telInfo.setRotPosLimit(Math.toRadians(2700.0)); //rads -cfg

	    telInfo.setClientDescriptor(new ClientDescriptor("/RCS",
							     ClientDescriptor.RCS_CLIENT,
							     ClientDescriptor.RCS_PRIORITY));
	    telInfo.setCrypto(new Crypto("/RCS"));
	    
	    sendCommand(telInfo);

	    // INST_INFO
	    INSTRUMENT_INFO instInfo = new INSTRUMENT_INFO("INST:"+count);
	    instInfo.setInstName("RATCAM");

	    CCDConfig ic = CCDConfig.getDefault();
	    instInfo.setConfig(ic);
	    instInfo.setCurrent(true);
	    instInfo.setStatus(Instrument.ONLINE);

	    instInfo.setClientDescriptor(new ClientDescriptor("/RCS",
                                                          ClientDescriptor.RCS_CLIENT,
							      ClientDescriptor.RCS_PRIORITY));
	    instInfo.setCrypto(new Crypto("/RCS"));	
	   	    	    
	    sendCommand(instInfo);

	    // SCHEDULE
	    long maxIdle    = 45*60*1000L;
	    long latestTime = System.currentTimeMillis() + 16*3600*1000L;
	    long deltaTime  = 15*60*1000L;
	    SCHEDULE schedule = new SCHEDULE("SCHED:"+count, 
					     maxIdle, 
					     latestTime, 
					     deltaTime, 
					     "OPTIMAL", 
					     false, 
					     "SIM");

	    schedule.setClientDescriptor(new ClientDescriptor("/RCS",
							      ClientDescriptor.RCS_CLIENT,
                                                              ClientDescriptor.RCS_PRIORITY));
	    schedule.setCrypto(new Crypto("/RCS"));
	    	   
	    valid = false; // unless we get a real reply..

	    TRANSACTION_DONE done = sendCommand(schedule);

	    if (!(done instanceof SCHEDULE_DONE)) continue;


	    SCHEDULE_DONE sched = (SCHEDULE_DONE)done;
	    
	    if (sched == null) continue;

	    // Unpack the descriptor and wait a while for the group to be done.

	    Group group = sched.getGroup();
	    
	    if (group == null) continue;
	    
	    // Got this far so its a Group of some sort.
	    valid = true;

	    System.err.println("Schedule Description:"+			      
			       "\n\tGroup: "+(group != null ? group.getFullPath()+" : "+
					      Integer.toHexString(group.getLock()).toUpperCase() : "NO GROUP")+
			       "\n\tScore: "+sched.getScore());			       
	    
	    Scheduling.calculateExecutionTimes(group);

	    Iterator iob = group.listAllObservations();
	    while (iob.hasNext()) {
		Observation obs = (Observation)iob.next();
		System.err.println("Obs> "+obs);
	    }
	    
	    
	    long delay = (long)group.getNominalExecutionTime();
	    
	  
	    boolean success = (Math.random() < 0.9);
	    if (! success)
		delay = (long)(Math.random()*delay);
	    
	    System.err.println("Group will: "+(success ? "COMPLETE" : "FAIL")+" after: "+(delay/1000)+" secs.");
	    
	    try {Thread.sleep(delay);} catch (InterruptedException ix) {}
	    
	    currAz  = Math.toRadians(Math.random()*360.0);
	    currAlt = Math.toRadians(Math.random()*90.0);
	    
	    
	    // UPDATE
	    String message = "";
	    int    code    = 0;
	    if (success) {
		message = "Completed in a most satisfactory manner";
		code   = 0;
	    } else {
		message = "Aborted with good cause";
		code    = 666777;
	    }
	
	    UPDATE_GROUP update = new UPDATE_GROUP("UPD:"+count,
						   new Path(group.getFullPath()), 
						   System.currentTimeMillis(), 
						   0xABAB00CC, 
						   success, 
						   message,
						   code,
						   (int)(delay/1000),
						   0L);
	   
	    update.setClientDescriptor(new ClientDescriptor("/RCS",
							    ClientDescriptor.RCS_CLIENT,
							    ClientDescriptor.RCS_PRIORITY));
	    update.setCrypto(new Crypto("/RCS"));
	    
	    // lets not send this as we are only testing it///
	    // no lets do just that boyo !
	    sendCommand(update);
	    
	}

    }

    /** Sends a command and waits for the response or handler to fail.*/
    public TRANSACTION_DONE sendCommand(TRANSACTION command) {

	 // Pass on to a Handler.
	Handler handler = new Handler(command);
	
	JMSMA_ProtocolClientImpl protocol = 
	    new JMSMA_ProtocolClientImpl((JMSMA_Client)handler, 
					 new SocketConnection(proxyHost, proxyPort));
	
	System.err.println("**Sending command to OSS: \n.."+command);
	
	protocol.implement();	

	while (handler.mydone == null && handler.myerr == 0) {
	    try { Thread.sleep(2000L); } catch (InterruptedException ix) {}	    
	}

	if (handler.mydone instanceof TRANSACTION_DONE) {
	    System.err.println("**Response received: \n.."+handler.mydone);
	    return ((TRANSACTION_DONE)handler.mydone);
	} else {
	    System.err.println("**NO Valid Response received - got: "+handler.mydone);
	    return null;
	}
    }

    /** Temporarily handles comms with OSS.*/
    class Handler extends JMSMA_ClientImpl {
	
	public COMMAND_DONE mydone = null;

	public int myerr;

	Handler(COMMAND command){
	    super();
	    timeout = 600000L;
	    this.command = command;
	}
	
	public void failedConnect(Exception e) {	   	  
	    System.err.println("Failed connect: "+e);
	    myerr = 1;
	}
	
	public void failedDespatch(Exception e) {	   
	    System.err.println("Failed despatch: "+e);
	     myerr = 2;
	}
	
	public void failedResponse(Exception e) {	   
	    System.err.println("Failed response: "+e); 
	    myerr = 3;
	}
	
	public void exceptionOccurred(Object source, Exception e) {	   
	    System.err.println("Exception: Source: "+source+" Exc: "+e); 
	    myerr = 4;
	}

	public void handleAck(ACK ack) {	  
	    System.err.println("Received Ack: "+ack.getTimeToComplete()+" millis.");
	}
	
	public void handleDone(COMMAND_DONE done) {	   
	    System.err.println("Received Response: "+
			       "\n\tClass:  "+done.getClass().getName()+
			       "\n\tError:  "+done.getErrorNum()+
			       "\n\tString: "+done.getErrorString());	
	    mydone = done;
	    myerr  = done.getErrorNum();
	}

		
	public void sendCommand(COMMAND command) {}
	
    }

    class SeeingSimulator extends Thread {
	
	public void run() {

	    int ct = 0;

	    while (true) {

		ct++;

		long time  = System.currentTimeMillis();
		double val = 0.7 + (Math.random()*1.0);
	    
		UPDATE_SEEING update = new UPDATE_SEEING("SEEING:"+ct,
							 UPDATE_SEEING.UPDATE_SEEING,
							 val,
							 time);
		update.setClientDescriptor(new ClientDescriptor("/RCS",
								ClientDescriptor.RCS_CLIENT,
								ClientDescriptor.RCS_PRIORITY));
		update.setCrypto(new Crypto("/RCS"));
		
		sendCommand(update);

		// delay before next update
		try {Thread.sleep(60000L);} catch (InterruptedException ix) {}	
			
	    }

	}

    }
    

}
